package com.wutma.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wutma.api.domain.dto.JudgeSubmitDTO;
import com.wutma.api.domain.vo.UserExeResult;
import com.wutma.api.domain.vo.UserQuestionResultVO;
import com.wutma.common.core.constants.JudgeConstants;
import com.wutma.common.core.enums.CodeRunStatus;
import com.wutma.common.core.enums.QuestionResult;
import com.wutma.judge.domain.SandBoxExecuteResult;
import com.wutma.judge.domain.UserSubmit;
import com.wutma.judge.mapper.UserSubmitMapper;
import com.wutma.judge.service.IJudgeService;
import com.wutma.judge.service.ISandboxPoolService;
import com.wutma.judge.service.ISandboxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandboxService sandboxService;

    @Autowired
    private ISandboxPoolService sandboxPoolService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {

//        SandBoxExecuteResult sandBoxExecuteResult =
//                sandboxService.exeJavaCode(judgeSubmitDTO.getUserId(), judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());
//         这里使用容器池版本进行代码执行
        SandBoxExecuteResult sandBoxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(), judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();
        if (sandBoxExecuteResult != null  && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) {
            userQuestionResultVO = doJudge(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO);
        }
        else {
            // 进入此处，表示代码因未知异常执行错误
            userQuestionResultVO.setPass(QuestionResult.ERROR.getValue());
            if (sandBoxExecuteResult != null) {
                userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
            }
            else {
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }
        // 将提交代码保持到数据库中
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);
        return userQuestionResultVO;
    }

    private UserQuestionResultVO doJudge(JudgeSubmitDTO judgeSubmitDTO, SandBoxExecuteResult sandBoxExecuteResult, UserQuestionResultVO userQuestionResultVO) {
        List<String> executeResultOutputList = sandBoxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();
        if (executeResultOutputList.size() !=  outputList.size()) {
            // 输出结果与预期结果数量对不上，返回错误
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(QuestionResult.ERROR.getValue());
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }
        // 输出结果与预期结果数量对的上，进行内容判断
        List<UserExeResult> userExeResults = new ArrayList<>();
        boolean passed = resultCompare(judgeSubmitDTO, outputList, executeResultOutputList, userExeResults);
        return assembleUserResultVO(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO, passed, userExeResults);
    }

    private static UserQuestionResultVO assembleUserResultVO(JudgeSubmitDTO judgeSubmitDTO, SandBoxExecuteResult sandBoxExecuteResult, UserQuestionResultVO userQuestionResultVO, boolean passed, List<UserExeResult> userExeResults) {
        if (!passed) {
            // 进入此处，输出结果与预期结果内容存在不一致，返回错误
            userQuestionResultVO.setPass(QuestionResult.ERROR.getValue());
            userQuestionResultVO.setUserExeResultList(userExeResults);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }
        // 进入此处，开始比较时间和空间限制
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            userQuestionResultVO.setPass(QuestionResult.ERROR.getValue());
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getSpaceLimit()) {
            userQuestionResultVO.setPass(QuestionResult.ERROR.getValue());
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }
        // 进入此处，代码执行结果正确
        userQuestionResultVO.setPass(QuestionResult.PASS.getValue());
        userQuestionResultVO.setScore(JudgeConstants.DEFAULT_SCORE * judgeSubmitDTO.getDifficulty());
        userQuestionResultVO.setUserExeResultList(userExeResults);
        return userQuestionResultVO;
    }

    private boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO, List<String> outputList, List<String> executeResultOutputList, List<UserExeResult> userExeResults) {
        boolean passed = true;
        for (int index = 0; index < outputList.size(); index++) {
            String output = outputList.get(index);
            String exeOutput = executeResultOutputList.get(index);
            String input = judgeSubmitDTO.getInputList().get(index);
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutput);
            userExeResults.add(userExeResult);
            if (!output.equals(exeOutput)) {
                passed = false;
            }
        }
        return passed;
    }

    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        UserSubmit userSubmit = new UserSubmit();
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        userSubmitMapper.insert(userSubmit);
    }
}
